﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

using Amazon.SimpleDB;
using Amazon.SimpleDB.Model;
using Amazon.SimpleDB.Util;

namespace PennyStorm.Util
{
    //
    // There are some basic objects we need to create over and over again; this factory class does it for us
    //
    public class AmazonSimpleDBFactory
    {
        public static AmazonSimpleDBFactory Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new AmazonSimpleDBFactory();
                    System.Threading.Thread.MemoryBarrier();
                }

                return _instance;
            }
        }

        ~AmazonSimpleDBFactory()
        {
            //
            // Need to dispose because SecureString is used internally
            //
            _client.Dispose();
        }

        public AmazonSimpleDBClient Client
        {
            get { return _client; }
        }

        public string GetStringValue(string itemName, string attributeName)
        {
            GetAttributesRequest getAttributes = this.MakeGetAttributesRequest(true);
            getAttributes.ItemName = itemName;
            getAttributes.AttributeName.Add(attributeName);

            string value = string.Empty;
            GetAttributesResponse response = _client.GetAttributes(getAttributes);

            //
            // Get our value
            //
            if (response.IsSetGetAttributesResult())
            {
                if (response.GetAttributesResult.Attribute.Count > 0)
                {
                    value = response.GetAttributesResult.Attribute[0].Value;
                }
            }

            return value;
        }

        public int GetIntegerValue(string itemName, string attributeName)
        {

            GetAttributesRequest getAttributes = this.MakeGetAttributesRequest(true);
            getAttributes.ItemName = itemName;
            getAttributes.AttributeName.Add(attributeName);

            int originalValue = 0;
            GetAttributesResponse response = _client.GetAttributes(getAttributes);

            //
            // Get our value
            //
            if (response.IsSetGetAttributesResult())
            {
                if (response.GetAttributesResult.Attribute.Count > 0)
                {
                    originalValue = Int32.Parse(response.GetAttributesResult.Attribute[0].Value);
                }
            }

            return originalValue;
        }

        public void SetIntegerValue(string itemName, string attributeName, int value)
        {
            PutAttributesRequest putAttributes = this.MakePutAttributesRequest();
            putAttributes.ItemName = itemName;
            
            putAttributes.Attribute.Add(this.MakeReplaceableAttribute(attributeName, value.ToString(), true));

            _client.PutAttributes(putAttributes);
        }
                
        /// <summary>
        /// We do this pattern everywhere - first read the value consistently, then do a conditional update
        //  based on that value. 
        /// </summary>
        /// <param name="itemName"></param>
        /// <param name="attributeName"></param>
        /// <param name="increment"></param>
        /// <param name="amount"></param>
        /// <param name="originalValue"></param>
        /// <param name="newValue"></param>
        /// <returns></returns>
        public bool ConsistentSwapInteger(string itemName, string attributeName, bool increment, int amount, out int originalValue, out int newValue)
        {
            bool success = false;

            try
            {
                originalValue = this.GetIntegerValue(itemName, attributeName);
                
                //
                // Increment or decrement this value by 1 and then set it
                //                
                newValue = increment == true ? (originalValue + amount) : (originalValue - amount);

                UpdateCondition updateCondition = new UpdateCondition();
                updateCondition.Name = attributeName;
                updateCondition.Value = originalValue.ToString();

                PutAttributesRequest putAttributes = this.MakePutAttributesRequest();

                putAttributes.Expected = updateCondition;
                putAttributes.ItemName = itemName;
                putAttributes.Attribute.Add(this.MakeReplaceableAttribute(attributeName, newValue.ToString(), true));

                _client.PutAttributes(putAttributes);
                
                success = true;
            }
            catch
            {
                originalValue = 0;
                newValue = 0;
            }

            return success;
        }

        public ReplaceableAttribute MakeReplaceableAttribute(string name, string value, bool replace)
        {
            ReplaceableAttribute attribute = new ReplaceableAttribute();
            attribute.Replace = replace;
            attribute.Name = name;
            attribute.Value = value;

            return attribute;
        }

        public PutAttributesRequest MakePutAttributesRequest()
        {
            PutAttributesRequest putAttributes = new PutAttributesRequest();            
            putAttributes.DomainName = PennyStormUtil.Properties.Settings.Default.domain;

            return putAttributes;
        }

        public PutAttributesRequest MakePutAttributesRequestForUser(Controller controller)
        {
            PutAttributesRequest putAttributes = this.MakePutAttributesRequest();
            putAttributes.ItemName = controller.HttpContext.User.Identity.Name;

            return putAttributes;
        }

        public GetAttributesRequest MakeGetAttributesRequest(bool consistent)
        {
            GetAttributesRequest getAttributes = new GetAttributesRequest();
            getAttributes.DomainName = PennyStormUtil.Properties.Settings.Default.domain;
            getAttributes.ConsistentRead = consistent;

            return getAttributes;
        }

        public GetAttributesRequest MakeGetAttributesRequestForUser(bool consistent, Controller controller)
        {
            GetAttributesRequest getAttributes = this.MakeGetAttributesRequest(consistent);
            getAttributes.ItemName = controller.User.Identity.Name;

            return getAttributes;
        }

        AmazonSimpleDBClient _client;
        private static AmazonSimpleDBFactory _instance = null;

        private AmazonSimpleDBFactory()
        {
            _client = new AmazonSimpleDBClient(PennyStormUtil.Properties.Settings.Default.key, PennyStormUtil.Properties.Settings.Default.secret);
        }
    }
}
